package org.dru.clay.util.functional;

import org.dru.clay.util.FileMatcherPredicate;

/**
 * Predicates
 * User: joakimd
 * Date: 2013-07-22
 * Time: 15:49
 */
public final class Predicates {
    private Predicates() {
    }

    public static <T> Predicate<T> alwaysTrue() {
        return ObjectPredicate.ALWAYS_TRUE.withNarrowedType();
    }

    public static <T> Predicate<T> alwaysFalse() {
        return ObjectPredicate.ALWAYS_FALSE.withNarrowedType();
    }

    public static <T> Predicate<T> notNull() {
        return ObjectPredicate.NOT_NULL.withNarrowedType();
    }

    public static <T> Predicate<T> isNull() {
        return ObjectPredicate.IS_NULL.withNarrowedType();
    }

    public static <T> Predicate<T> equalTo(final T value) {
        return new IsEqualPredicate<T>(value);
    }

    public static <T> Predicate<T> not(final Predicate<T> predicate) {
        return new NotPredicate<T>(predicate);
    }
    
    public static Predicate<String> pattern(String pattern) {
    	return new FileMatcherPredicate(pattern);
    }
    
    public static <F, T> Predicate<F> composite(final Function<F, T> function, final Predicate<T> predicate) {
        return new Predicate<F>() {
            @Override
            public boolean apply(final F value) {
                return predicate.apply(function.apply(value));
            }
        };
    }

    private static enum ObjectPredicate implements Predicate<Object> {
        ALWAYS_TRUE {
            @Override
            public boolean apply(final Object value) {
                return true;
            }
        },
        ALWAYS_FALSE {
            @Override
            public boolean apply(final Object value) {
                return false;
            }
        },
        NOT_NULL {
            @Override
            public boolean apply(final Object value) {
                return value != null;
            }
        },
        IS_NULL {
            @Override
            public boolean apply(final Object value) {
                return value == null;
            }
        };

        @SuppressWarnings("unchecked")
        public <T> Predicate<T> withNarrowedType() {
            return (Predicate<T>) this;
        }
    }

    private static class IsEqualPredicate<T> implements Predicate<T> {
        private final T value;

        private IsEqualPredicate(final T value) {
            value.getClass();
            this.value = value;
        }

        @Override
        public boolean apply(final T value) {
            return this.value.equals(value);
        }
    }

    private static class NotPredicate<T> implements Predicate<T> {
        private final Predicate<T> predicate;

        private NotPredicate(final Predicate<T> predicate) {
            predicate.getClass();
            this.predicate = predicate;
        }

        @Override
        public boolean apply(final T value) {
            return !predicate.apply(value);
        }
    }
}
